package org.utn.ia.services;

/**
 * Calculamos por cada nodo el costo = suma de las piedras del jugadro B * 10 - la suma de las piedras del jugador A * 10;
 * Elegiremos el que tiene menor puntaje en la hoja que es el jugador MIN
 * 
 */
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.utn.ia.init.TemplateMancala;
import org.utn.ia.mancala.Arbol;
import org.utn.ia.mancala.Mancala;
import org.utn.ia.mancala.Nodo;
import org.utn.ia.strategies.UtilidadByPiedras;
import org.utn.ia.strategies.UtilidadByPuntaje;
import org.utn.ia.strategies.UtilidadStrategy;
import org.utn.ia.utils.Constants;
import org.utn.ia.utils.Utils;

public class ArbolService {
	private static String TIPO_PROFUNDIDAD_INI = "INI";
	private static String TIPO_PROFUNDIDAD_MAX = "MAX";
	private static String TIPO_PROFUNDIDAD_MIN = "MIN";
	private static int COSTO_CAMINO_INICIAL = 0;

	public static Boolean SOLUCION = false;

	private Arbol arbol;

	public Arbol getArbol() {
		return arbol;
	}

	public void setArbol(Arbol arbol) {
		this.arbol = arbol;
	}

	public Arbol generarArbol(int nivel) {

		if (Mancala.getInstance().getEstado() == null) {
			inicializarEstadoMancala();
		}

		Nodo nodoRaiz = new Nodo();
		nodoRaiz.setMancala(Mancala.getInstance());
		nodoRaiz.setCostoCamino(COSTO_CAMINO_INICIAL);
		nodoRaiz.setProfundidad(0);
		arbol = new Arbol();
		// Verificamos que mancala no este en su estado final,
		// si no es estado final, entoces expandimos
		if (!esMancalaFinal(Mancala.getInstance())) {
			nodoRaiz.setTipoProfundidad(TIPO_PROFUNDIDAD_INI);
			nodoRaiz.setSubnodos(expandirNodo(nodoRaiz, nivel));
			arbol.setRaiz(nodoRaiz);
		}else{
			
			Utils.setGanador(Mancala.getInstance().getEstado());
			System.out.print(" Mancala Ganador: "
					+ Arrays.toString(Mancala.getInstance().getEstado()));
			Constants.MANCALA_SOLUCION = true;
		}

		return getArbol();
	}

	private void inicializarEstadoMancala() {
		TemplateMancala.setEstadoInicial(0);
	}

	private List<Nodo> expandirNodo(Nodo nodo, int nivel) {
		Constants.CANTIDAD_NODOS_EXPANDIDO = Constants.CANTIDAD_NODOS_EXPANDIDO + 1;
		if (nodo.getProfundidad() == nivel
				&& nodo.getTipoProfundidad() == TIPO_PROFUNDIDAD_MIN) {
			return null;
		}
		int limiteInferior, limiteSuperior;
		if (nodo.getTipoProfundidad() == TIPO_PROFUNDIDAD_INI
				|| nodo.getTipoProfundidad() == TIPO_PROFUNDIDAD_MIN) {
			limiteInferior = 0;
			limiteSuperior = 6;
		} else {
			limiteInferior = 7;
			limiteSuperior = 13;
		}

		int[] estadoMancala = nodo.getMancala().getEstado();
		List<Nodo> subnodos = null;

		for (int i = limiteInferior; i < limiteSuperior; i++) {

			if (estadoMancala[i] != 0) {
				if (subnodos == null)
					subnodos = new ArrayList<Nodo>();
				Mancala mancala = calcularMancala(estadoMancala, i);
				//Si mancala sin expandir 
				// si no es estado final entonces expandimos los subnodos
				if (!esMancalaFinal(mancala)) {
					Nodo subnodo = new Nodo();
					subnodo.setMancala(mancala);
					subnodo.setPadre(nodo);
					subnodo.setProfundidad(calcularProfundidad(nodo));
					subnodo.setTipoProfundidad(calcularTipoProfundidad(nodo));
					subnodo.setCostoCamino(calcularCostoCamino(nodo));
					subnodo.setSubnodos(expandirNodo(subnodo, nivel));
					subnodo.setUtilidad(calcularUtilidad(subnodo));
					subnodos.add(subnodo);
				} else {
					Mancala.getInstance().setEstado(mancala.getEstado());
					return subnodos;
				}

			}
		}
		nodo.setSubnodos(subnodos);

		return subnodos;
	}

	private Mancala calcularMancala(int[] estado, int indice) {
		Mancala mancala = new Mancala();
		int[] nuevoEstado = clonarEstadoMancala(estado);

		int maxIteraciones = nuevoEstado[indice];
		nuevoEstado[indice++] = 0;
		for (int i = 0; i < maxIteraciones; i++) {
			nuevoEstado[indice % 14] = nuevoEstado[(indice++) % 14] + 1;
		}

		mancala.setEstado(nuevoEstado);
		return mancala;
	}

	private int[] clonarEstadoMancala(int[] estado) {
		int[] array = new int[estado.length];
		for (int i = 0; i < estado.length; i++) {
			array[i] = estado[i];
		}
		return array;
	}

	private int calcularProfundidad(Nodo nodo) {
		if (nodo.getTipoProfundidad() == TIPO_PROFUNDIDAD_INI
				|| nodo.getTipoProfundidad() == TIPO_PROFUNDIDAD_MIN) {
			return nodo.getProfundidad() + 1;
		} else {
			return nodo.getProfundidad();
		}
	}

	private String calcularTipoProfundidad(Nodo nodo) {
		if (nodo.getTipoProfundidad() == TIPO_PROFUNDIDAD_INI
				|| nodo.getTipoProfundidad() == TIPO_PROFUNDIDAD_MIN) {
			return TIPO_PROFUNDIDAD_MAX;
		} else {
			return TIPO_PROFUNDIDAD_MIN;
		}
	}

	private int calcularUtilidad(Nodo nodo) {
		List<Nodo> subnodos = nodo.getSubnodos();
		if (subnodos == null) { // Nodo Hoja
			// funcion sucesor
			UtilidadStrategy strategia = new UtilidadByPuntaje();
			return strategia.calcularUtilidad(nodo);
		} else {
			/**
			 * El nodo Max/Min/Inicial tiene subnodos asi que deberiamos solo
			 * obtener el valor min o max
			 */
			int max = 0, min = 0;
			for (Nodo subnodo : subnodos) {
				if (subnodo.getUtilidad() < min) {
					min = subnodo.getUtilidad();
				}

				if (subnodo.getUtilidad() > max) {
					max = subnodo.getUtilidad();
				}
			}

			if (nodo.getTipoProfundidad() == TIPO_PROFUNDIDAD_MIN
					|| nodo.getTipoProfundidad() == TIPO_PROFUNDIDAD_INI) {
				return max;
			} else {
				return min;
			}
		}
	}

	private int calcularCostoCamino(Nodo nodo) {
		int[] e = nodo.getMancala().getEstado();
		int cantPiedrasB = e[7] + e[8] + e[9] + e[10] + e[11] + e[12];
		int cantPiedrasA = e[0] + e[1] + e[2] + e[3] + e[4] + e[5];
		int puntaje = cantPiedrasB * 10 - cantPiedrasA * 10;

		return nodo.getCostoCamino() + puntaje;
	}

	private boolean esMancalaFinal(Mancala mancala) {
		int[] estado = mancala.getEstado();
		boolean esFinal = false;
		int totalPiedrasJug1 = estado[0] + estado[1] + estado[2] + estado[3]
				+ estado[4] + estado[5];
		int totalPiedrasJug2 = estado[7] + estado[8] + estado[9] + estado[10]
				+ estado[11] + estado[12];
		if (totalPiedrasJug1 == 0 || totalPiedrasJug2 == 0) {
			esFinal = true;
		} else {
			esFinal = false;
		}
		return esFinal;
	}

	public Mancala getMancalaResultado(Arbol arbol) {
		Mancala result = null;

		return result;
	}

}
